package com.eh.json;

import android.support.annotation.NonNull;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

/**
 * Created by zhangxiaowei on 17/3/3.
 */
public class JsonUtil {
    public static <T> T parseObject(String data, Class<T> clazz) {
        T obj = null;
        try {
            JSONObject jsonObject = new JSONObject(data);
            obj = (T) toObj(jsonObject, clazz);

        } catch (Exception e) {
            e.printStackTrace();
        }
        return obj;
    }

    public static  <T> List<T> parseArray(String data, Class<T> clazz) {
        List<T> list = null;
        try {

            JSONArray array = new JSONArray(data);
            if (array == null || array.length() == 0) {
                return null;
            }
            list = new ArrayList();
            for (int i = 0; i < array.length(); i++) {
                JSONObject object = array.getJSONObject(i);
                T object1 = (T) toObj(object, clazz);
                list.add(object1);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return list;
    }

    private static Object toObj(JSONObject data, Class<?> clazz) {
        try {
            if (data == null || clazz == null||clazz.isAssignableFrom(JSONObject.class) || clazz.getName().equals(Object.class.getName()) ) {
                return data;
            }
            Object obj = clazz.newInstance();
            Field[] declaredFields = clazz.getDeclaredFields();
            for (int i = 0; i < declaredFields.length; i++) {
                Field field = declaredFields[i];
                field.setAccessible(true);
                String name = field.getName();
                Class<?> fieldType = field.getType();
                /**
                 * 基本数据类型
                 */
                if (fieldType.isPrimitive()) {

                    if ((Integer.TYPE == fieldType) || (Integer.class == fieldType)) {
                        field.set(obj, data.optInt(name));
                        continue;
                    }
                    if ((Long.TYPE == fieldType) || (Long.class == fieldType)) {
                        field.set(obj, data.optLong(name));
                        continue;
                    }
                    if ((Float.TYPE == fieldType) || (Float.class == fieldType)) {
                        field.set(obj, data.optLong(name));
                        continue;
                    }
                    if ((Double.TYPE == fieldType) || (Double.class == fieldType)) {
                        field.set(obj, data.optDouble(name));
                        continue;
                    }
                    if ((Short.TYPE == fieldType) || (Short.class == fieldType)) {
                        field.set(obj, data.optInt(name));
                        continue;
                    }
                    continue;
                } else if (String.class == fieldType) {
                    field.set(obj, data.optString(name));
                    continue;
                } else
                /**
                 * LIST数组
                 */
                    if (fieldType.isAssignableFrom(List.class)) {
                        Type fc = field.getGenericType(); // 如果是List类型，得到其Generic的类型
                        List list = null;
                        list = new ArrayList();
                        JSONArray array = data.optJSONArray(name);
                        try {
                            listType(array, list, fc);
                            field.set(obj, list);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }

                        continue;
                    } else if (fieldType.isAssignableFrom(Map.class)) {

                        HashMap<String, String> data1 = new HashMap<String, String>();
                        // 将json字符串转换成jsonObject
                        JSONObject jsonObject = data.optJSONObject(name);
                        Iterator it = jsonObject.keys();
                        // 遍历jsonObject数据，添加到Map对象
                        while (it.hasNext()) {
                            String key = String.valueOf(it.next());
                            String value = (String) jsonObject.get(key);

                            data1.put(key, value);
                        }
                        continue;
                    }

                Object object12 = toObj(data.optJSONObject(name), fieldType);
                field.set(obj, object12);
            }
            return obj;
        } catch (Exception e)

        {
            e.printStackTrace();
            return null;
        }

    }

    private static void listType(JSONArray jsonArray, List list, Type fc) throws JSONException, IllegalAccessException {
        if (jsonArray != null) {
            if (fc == null) return;
            if (fc instanceof ParameterizedType) // 如果是泛型参数的类型
            {
                ParameterizedType pt = (ParameterizedType) fc;
                Type genericClazz1 = (Type) pt.getActualTypeArguments()[0]; // 得到泛型里的class类型对象。

                Class claz1 = null;
                String stClaz = genericClazz1.toString();
                try {
                    stClaz = getClassString(stClaz);
                    claz1 = Class.forName(stClaz);
                } catch (Exception e) {
                    e.printStackTrace();
                    return;
                }
                for (int j = 0; j < jsonArray.length(); j++) {

                    if (claz1.isAssignableFrom(Map.class)) {
                        Map map = null;
                        if (claz1.isAssignableFrom(Hashtable.class)) {
                            map = new Hashtable();
                        } else {
                            map = new HashMap();
                        }
                        JSONObject object1 = jsonArray.getJSONObject(j);
                        initMapType(object1, map, genericClazz1);
                        list.add(map);
                    } else if (claz1.isPrimitive()) {
                        if ((Integer.TYPE == claz1) || (Integer.class == claz1)) {
                            list.add(jsonArray.getInt(j));
                            continue;
                        }
                        if ((Long.TYPE == claz1) || (Long.class == claz1)) {
                            list.add(jsonArray.getLong(j));
                            continue;
                        }
                        if ((Float.TYPE == claz1) || (Float.class == claz1)) {
                            list.add(jsonArray.getLong(j));
                            continue;
                        }
                        if ((Double.TYPE == claz1) || (Double.class == claz1)) {
                            list.add(jsonArray.getDouble(j));
                            continue;
                        }
                        if ((Short.TYPE == claz1) || (Short.class == claz1)) {
                            list.add(jsonArray.getInt(j));
                            continue;
                        }
                        continue;
                    } else if (claz1.isAssignableFrom(String.class)) {
                        JSONObject object1 = jsonArray.getJSONObject(j);
                        list.add(object1.toString());

                    } else if (claz1.isAssignableFrom(List.class)) {
                        JSONArray array = jsonArray.getJSONArray(j);

                        ParameterizedType pttt = (ParameterizedType) genericClazz1;
                        Type generi = pttt.getActualTypeArguments()[0];

                    } else {
                        JSONObject object1 = jsonArray.getJSONObject(j);
                        Object objj = toObj(object1, claz1);
                        list.add(objj);
                    }
                }
            }
        }

    }

    @NonNull
    private static String getClassString(String stClaz) {
        stClaz = stClaz.replace(" ", "");
        if (stClaz.startsWith("class")) {
            stClaz = stClaz.substring(5, stClaz.length());
        }
        if (stClaz.contains("<") || stClaz.contains(",")) {
            int first = stClaz.indexOf("<");
            stClaz = stClaz.substring(0, first);
        }
        return stClaz;
    }

    /**
     * @param fc
     * @throws JSONException
     * @throws IllegalAccessException
     */
    private static void initMapType(JSONObject object, Map map, Type fc) {
        if (fc == null || object == null) return;
        if (fc instanceof ParameterizedType) // 如果是泛型参数的类型
        {
            //第一层数据类型
            ParameterizedType pt = (ParameterizedType) fc;
            Type genericClazz1 = (Type) pt.getActualTypeArguments()[1]; // 得到泛型里的class类型对象。
            Class<?> claz2 = null;

            String stClaz = genericClazz1.toString();
            try {
                stClaz = getClassString(stClaz);
                claz2 = Class.forName(stClaz);
            } catch (Exception e) {
                e.printStackTrace();
            }
            //第一层若还是泛型，则继续向下走
            //第二层泛型参数个数

            Iterator<String> iterator = object.keys();
            while (iterator.hasNext()) {
                String name = iterator.next();
                if (claz2.isAssignableFrom(List.class)) {
                    List list1 = null;
                    if (claz2.isAssignableFrom(LinkedList.class)) {
                        list1 = new LinkedList();
                    } else {
                        list1 = new ArrayList();
                    }


                    try {
                        JSONArray array = object.optJSONArray(name);
                        listType(array, list1, genericClazz1);
                        map.put(name, list1);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    continue;
                } else if (claz2.isAssignableFrom(Map.class)) {
                    Map map1 = null;
                    if (claz2.isAssignableFrom(Hashtable.class)) {
                        map1 = new Hashtable();
                    } else {
                        map1 = new HashMap();
                    }
                    JSONObject jsonObject = object.optJSONObject(name);
                    initMapType(jsonObject, map1, genericClazz1);
                    map.put(name, map1);
                } else if (claz2.isPrimitive()) {
                    if ((Integer.TYPE == claz2) || (Integer.class == claz2)) {
                        map.put(name, object.optInt(name));
                        continue;
                    }
                    if ((Long.TYPE == claz2) || (Long.class == claz2)) {
                        map.put(name, object.optLong(name));
                        continue;
                    }
                    if ((Float.TYPE == claz2) || (Float.class == claz2)) {
                        map.put(name, object.optLong(name));
                        continue;
                    }
                    if ((Double.TYPE == claz2) || (Double.class == claz2)) {
                        map.put(name, object.optDouble(name));
                        continue;
                    }
                    if ((Short.TYPE == claz2) || (Short.class == claz2)) {
                        map.put(name, object.optInt(name));
                        continue;
                    }
                    continue;
                } else if (claz2.isAssignableFrom(String.class)) {
                    map.put(name, object.optString(name));
                } else {
                    JSONObject jsonObject = object.optJSONObject(name);
                    Object object1 = toObj(jsonObject, claz2);
                    map.put(name, object1);
                }
            }

        }
    }

}
